#ifndef B812E0A1_6E06_4D69_9641_47972380FFC4
#define B812E0A1_6E06_4D69_9641_47972380FFC4
#include <complex>
#include <functional>

enum class FunctionType {
  polynomial,
  sinus,
  linear,
  scaled_logistic,
  hyperbola,
  sqrt_hyperbola,
};
class Func {
private:
  std::function<double(double)> f;
  double a;
  double b;
  FunctionType type;
  std::string string_representation;

  Func(std::function<double(double)> const &f, double a, double b,
       FunctionType type, std::string const &str = "")
      : f(f), a(a), b(b), type(type), string_representation(str){};

public:
  static Func *create_polynomial() {
    return new Func(
        [](double x) {
          return std::pow(x, 3) - 3 * std::pow(x, 2) + 7 * x - 10;
        },
        -INFINITY, INFINITY, FunctionType::polynomial, "x^3 - 3x^2 + 7x - 10");
  }

  static Func *create_sinus() {
    return new Func([](double x) { return std::sin(x); }, -INFINITY, INFINITY,
                    FunctionType::sinus, "sin(x)");
  }

  static Func *create_linear() {
    return new Func([](double x) { return 2 * x; }, -INFINITY, INFINITY,
                    FunctionType::linear, "2x");
  }

  static Func *create_scaled_logistic() {
    return new Func([](double x) { return 1 / (1 + std::exp(x)); }, -INFINITY,
                    INFINITY, FunctionType::scaled_logistic,
                    "1 / (1 + exp(x))");
  }

  static Func *create_hyperbola() {
    return new Func([](double x) { return 1 / x; }, -INFINITY, INFINITY,
                    FunctionType::hyperbola, "1 / x");
  }

  static Func *create_sqrt_hyperbola() {
    return new Func([](double x) { return 1 / std::sqrt(x); }, 0, INFINITY,
                    FunctionType::sqrt_hyperbola, "1 / sqrt(x)");
  }

  static Func *create_by_type(FunctionType type) {
    switch (type) {
    case FunctionType::polynomial:
      return create_polynomial();
    case FunctionType::sinus:
      return create_sinus();
    case FunctionType::linear:
      return create_linear();
    case FunctionType::scaled_logistic:
      return create_scaled_logistic();
    case FunctionType::hyperbola:
      return create_hyperbola();
    case FunctionType::sqrt_hyperbola:
      return create_sqrt_hyperbola();
    default:
      return nullptr;
    }
  }

  bool operator==(Func const &other) const {
    return type == other.type &&
           string_representation == other.string_representation;
  }

  double get_a() const { return a; }
  double get_b() const { return b; }
  FunctionType get_type() const { return type; }
  std::function<double(double)> get_function() const { return f; }
  std::pair<double, double> get_domain() const { return {a, b}; };
  std::string get_function_representation() const {
    return string_representation;
  }
  std::string get_function_type() const {
    switch (type) {
    case FunctionType::polynomial:
      return "polynomial";
    case FunctionType::sinus:
      return "sinus";
    case FunctionType::linear:
      return "linear";
    case FunctionType::scaled_logistic:
      return "scaled logistic";
    case FunctionType::hyperbola:
      return "hyperbola";
    case FunctionType::sqrt_hyperbola:
      return "sqrt hyperbola";
    default:
      return "unknown";
    }
  }
};

#endif /* B812E0A1_6E06_4D69_9641_47972380FFC4 */
